package by.brest.bspc.reporter.jasperreports;

import by.brest.bspc.reporter.jasperreports.connectors.MongoConnection;
import by.brest.bspc.reporter.jasperreports.connectors.MongoConnectionFactoryImpl;
import com.jaspersoft.mongodb.MongoDbDataSource;
import net.sf.jasperreports.engine.JRPrintElement;
import net.sf.jasperreports.engine.JRPrintPage;
import net.sf.jasperreports.engine.JRPrintText;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.export.JRXlsExporterParameter;
import net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.Properties;

import static by.brest.bspc.reporter.ReporterConstants.AVG;
import static by.brest.bspc.reporter.ReporterConstants.AVG_ELEMENT_WIDTH;
import static by.brest.bspc.reporter.ReporterConstants.DIRESPECT_TRUANCIES;
import static by.brest.bspc.reporter.ReporterConstants.DISCIPLINE_NAME;
import static by.brest.bspc.reporter.ReporterConstants.EXAM_ELEMENT;
import static by.brest.bspc.reporter.ReporterConstants.EXCEL_EXTENSION;
import static by.brest.bspc.reporter.ReporterConstants.HEADER;
import static by.brest.bspc.reporter.ReporterConstants.JASPER_EXTENSION;
import static by.brest.bspc.reporter.ReporterConstants.MARK;
import static by.brest.bspc.reporter.ReporterConstants.PRACTICE_ELEMENT;
import static by.brest.bspc.reporter.ReporterConstants.PRACTICE_ELEMENT_HEIGHT;
import static by.brest.bspc.reporter.ReporterConstants.RESPECT_TRUANCIES;
import static by.brest.bspc.reporter.ReporterConstants.RESPECT_TRUANCIES_ELEMENT_WIDTH;
import static by.brest.bspc.reporter.ReporterConstants.SEMESTER_ELEMENT;
import static by.brest.bspc.reporter.ReporterConstants.TOTAL_TRUANCIES;
import static by.brest.bspc.reporter.ReporterConstants.TOTAL_TRUANCIES_ELEMENT_WIDTH;
import static by.brest.bspc.reporter.ReporterConstants.TRUANCIES;


/**
 * Class contains method, that fills report with data and extracts it to file.
 *
 * @date: 20.09.13 20:53
 * @author: Natallia
 */
public class JasperReportsCreatorImpl implements JasperReportsCreator {
    private static final Logger LOG = LoggerFactory.getLogger(JasperReportsCreatorImpl.class);

    private int examElementWidth = 0;
    private int semesterElementWidth = 0;
    private int semesterElementX = 0;
    private int lasElementX = 0;
    private int lasElementY = 0;

    /**
     * {@inheritDoc}
     */
    @Override
    public JasperPrint createReport(String sourceFile, Map<String, Object> parametersMap, String xlsxFile, String mongoUri) {
        LOG.debug(">> Starting createReport method with next parameters:");
        LOG.debug(">> sourceFileName: " + sourceFile);
        LOG.debug(">> parametersMap: " + parametersMap);
        LOG.debug(">> xlsxReportDirectory: " + xlsxFile);

        MongoConnection mongoConnection = new MongoConnectionFactoryImpl().createConnection(mongoUri);
        JasperPrint jasperPrint = null;

        try {
            parametersMap.put(MongoDbDataSource.CONNECTION, mongoConnection.getConnectionInstance());

            LOG.debug(">> Added mongoConnection to parameters map: " + mongoConnection);
            LOG.debug(">> Trying to fill report from template: " + sourceFile);

            jasperPrint = JasperFillManager.fillReport(sourceFile, parametersMap);
            if (sourceFile.contains("summary_report")) {
                summaryReportShiftElements(jasperPrint);
            }
            LOG.debug(">> JasperPrint file is received: " + jasperPrint);

            JRXlsxExporter exporter = new JRXlsxExporter();
            exporter.setParameter(JRXlsExporterParameter.JASPER_PRINT, jasperPrint);
            exporter.setParameter(JRXlsExporterParameter.OUTPUT_FILE_NAME, xlsxFile);

            LOG.debug(">> Export JasperPrint file to file: " + xlsxFile);

            exporter.exportReport();

            LOG.debug(">> Export finished.");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (mongoConnection.getConnectionInstance() != null) {
                mongoConnection.close();
            }
        }

        return jasperPrint;
    }

    /**
     * Calculates parameters for elements, that should be shifted.
     *
     * @param jasperPrint filled report.
     */
    private void summaryReportCalculateParameters(JasperPrint jasperPrint) {
        JRPrintPage jrPrintPage = jasperPrint.getPages().get(0);
        for (JRPrintElement jrPrintElement : jrPrintPage.getElements()) {
            if (jrPrintElement.getKey().contains(DISCIPLINE_NAME)) {
                JRPrintText jrPrintText = (JRPrintText) jrPrintElement;
                String fullText = jrPrintText.getFullText();
                if (fullText.contains("_" + EXAM_ELEMENT)) {
                    examElementWidth += jrPrintText.getWidth();
                    jrPrintText.setText(fullText.substring(0, fullText.indexOf("_" + EXAM_ELEMENT)));
                } else if (fullText.contains("_" + SEMESTER_ELEMENT)) {
                    semesterElementWidth += jrPrintText.getWidth();
                    if (semesterElementX == 0) {
                        semesterElementX = jrPrintText.getX();
                        LOG.debug(">> semester element x: " + semesterElementX);
                    }
                    jrPrintText.setText(fullText.substring(0, fullText.indexOf("_" + SEMESTER_ELEMENT)));
                } else if (fullText.contains("_" + PRACTICE_ELEMENT)) {
                    jrPrintText.setText(fullText.substring(0, fullText.indexOf("_" + PRACTICE_ELEMENT)));
                    jrPrintElement.setY(jrPrintElement.getY() - 20);
                    jrPrintText.setHeight(PRACTICE_ELEMENT_HEIGHT);
                }
                lasElementX = jrPrintElement.getX() + 17;
            } else if (jrPrintElement.getKey().contains(MARK)) {
                lasElementY = jrPrintElement.getY() + 14 > lasElementY ? jrPrintElement.getY() + 14 : lasElementY;
            }
        }
        lasElementX += 17;
        lasElementY += 14;

        LOG.debug(">> last element x: " + lasElementX + "; last element y: " + lasElementY);
        LOG.debug(">> exam element width: " + examElementWidth + "; semester element width: " + semesterElementWidth);
    }

    /**
     * Shifts elements.
     *
     * @param jasperPrint filled report.
     */
    private void summaryReportShiftElements(JasperPrint jasperPrint) {
        summaryReportCalculateParameters(jasperPrint);
        JRPrintPage jrPrintPage = jasperPrint.getPages().get(0);
        for (JRPrintElement jrPrintElement : jrPrintPage.getElements()) {
            switch (jrPrintElement.getKey()) {
                case EXAM_ELEMENT:
                    jrPrintElement.setWidth(examElementWidth);
                    break;
                case SEMESTER_ELEMENT:
                    jrPrintElement.setWidth(semesterElementWidth);
                    jrPrintElement.setX(semesterElementX);
                    break;
                case AVG:
                case AVG + HEADER:
                    jrPrintElement.setX(lasElementX);
                    break;
                case TRUANCIES:
                    jrPrintElement.setX(lasElementX + AVG_ELEMENT_WIDTH);
                    break;
                case TOTAL_TRUANCIES:
                case TOTAL_TRUANCIES + HEADER:
                    jrPrintElement.setX(lasElementX + AVG_ELEMENT_WIDTH);
                    break;
                case RESPECT_TRUANCIES:
                case RESPECT_TRUANCIES + HEADER:
                    jrPrintElement.setX(lasElementX + AVG_ELEMENT_WIDTH + TOTAL_TRUANCIES_ELEMENT_WIDTH);
                    break;
                case DIRESPECT_TRUANCIES:
                case DIRESPECT_TRUANCIES + HEADER:
                    jrPrintElement.setX(lasElementX + AVG_ELEMENT_WIDTH + TOTAL_TRUANCIES_ELEMENT_WIDTH
                            + RESPECT_TRUANCIES_ELEMENT_WIDTH);
            }
        }
    }
}